home *** CD-ROM | disk | FTP | other *** search
- #include <errno.h>
- #include <string.h>
- #include <unistd.h>
- #include <sys/stat.h>
- #include <sys/time.h>
- #include <sys/types.h>
- #if defined(__hpux__) || defined(__linux__)
- # include <sys/ioctl.h>
- #else
- # include <sys/filio.h>
- #endif
- #include <sys/socket.h>
-
- #include "relay.h"
-
-
- #define Export
-
-
- #define MIN(a,b) ((a) < (b) ? (a) : (b))
- #define MAX(a,b) ((a) > (b) ? (a) : (b))
-
-
- /*
- * Estimate how many bytes are available for reading on a file descriptor.
- */
- static long
- available_bytes(int fd)
- {
- long nbytes;
- struct stat sb;
- off_t pos;
-
- /*
- * Some Unices don't support the FIONREAD ioctl on regular files.
- * They don't even return an error, but says there are zero bytes
- * to be read. We then try to fstat() the file descriptor to see
- * how big the file is, and try to determine at what offset the
- * file pointer is at now. That doesn't work always either...
- */
-
- if (ioctl(fd, FIONREAD, &nbytes) < 0 || nbytes == 0)
- {
- if (fstat(fd, &sb) < 0)
- return -1;
- errno = 0;
- pos = lseek(fd, 0, SEEK_CUR);
- if ((pos < 0 && errno != 0) || !S_ISREG(sb.st_mode))
- pos = 0;
- nbytes = sb.st_size - pos;
- }
- return nbytes;
- }
-
-
-
- /*
- * Wait for data to arrive on any of the source file descriptors in
- * RELAYS, read all available data, and write to respective dest
- * file descriptors. TIMEOUT is the maximum time to wait, or a nil
- * pointer to never time out. For each read() done, the function
- * CALLBACK is called. Note that CALLBACK may be called more than
- * once for each file descriptor if much data is available.
- * NRELAYS is the number of elements in RELAYS.
- *
- * If the 'readerror' field is non-zero for a relay, no read will be
- * attempted from the source file descriptor of that relay.
- * If the 'writerror' field is non-zero for a relay, no write will be
- * attempted to the dest file descriptor of that relay, but the source
- * descriptor will still be drained from any available data.
- *
- * If end-of-file is reached on the source of a relay, the corresponding
- * destination will be shutdown() for writing, thus causing the other
- * end to see a end-of-file.
- * If a write error occurs on the destination of a relay, the source
- * of that relay will be shutdown() for reading, causing the other end
- * to get errors when trying to write more data to us.
- * Note that the SIGPIPE signal should be ignored, or possibly caught,
- * by the caller, or the process will die when a receiver closes its
- * end for receiving.
- *
- * Returns the number of file descriptor read from, or negative on error.
- */
- Export int
- relay_once(struct relay * relays,
- int nrelays,
- struct timeval * timeout,
- int (*callback)(struct relay*, char*, size_t)
- )
- {
- int maxfd;
- fd_set readset;
- int i;
- int nfds;
- int nerrors = 0;
-
- FD_ZERO(&readset);
- maxfd = 0;
- for (i = nrelays - 1 ; i >= 0 ; i--)
- {
- if (!relays[i].readerror) {
- FD_SET(relays[i].source, &readset);
- if (relays[i].source > maxfd)
- maxfd = relays[i].source;
- }
- }
-
- nfds = select(maxfd+1, &readset, (fd_set*)NULL, (fd_set*)NULL, timeout);
- if (nfds <= 0) {
- if (errno != EINTR)
- return nfds;
- else
- return 0;
- }
-
- for (i = nrelays - 1 ; i >= 0 ; i--)
- {
- if (FD_ISSET(relays[i].source, &readset))
- {
- long unread = available_bytes(relays[i].source);
- do
- {
- char buffer[8192];
- int bytes_read = read(relays[i].source, buffer, sizeof buffer);
-
- if (bytes_read < 0) {
- relays[i].readerror = errno;
- nerrors++;
- break;
- }
- unread -= bytes_read;
- if (bytes_read == 0) {
- relays[i].readerror = -1;
- shutdown(relays[i].dest, 1);
- nerrors++;
- }
- if (callback)
- (*callback)(&relays[i], buffer, bytes_read);
- if (!relays[i].writeerror && bytes_read > 0) {
- int written = write(relays[i].dest, buffer, bytes_read);
- if (written < 0) {
- relays[i].writeerror = errno;
- shutdown(relays[i].source, 0);
- nerrors++;
- break;
- }
- }
- } while (unread > 0);
- }
- }
-
- return nerrors ? -nerrors : nfds;
- }
-
-
-
- /*
- * Call relay_once() until end-of-file has been reached on all sources.
- * No time limit.
- */
- Export int
- relay_all(struct relay * relaylist,
- int nrelays,
- int (*callback)(struct relay*, char*, size_t))
- {
- int nclosed = 0;
- do
- {
- int status = relay_once(relaylist, nrelays, NULL, callback);
- if (status < 0)
- nclosed += -status;
- } while (nclosed < nrelays);
-
- return 0;
- }
-